package com.shchuangzhi.smartguard.core.command;

import com.elvishew.xlog.XLog;
import com.elvishew.xlog.interceptor.Interceptor;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import com.shchuangzhi.smartguard.core.Consts;
import com.shchuangzhi.smartguard.core.DBHelper;
import com.shchuangzhi.smartguard.core.InOutRecord;
import com.shchuangzhi.smartguard.core.Machine;
import com.shchuangzhi.smartguard.core.NotifyMessage;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by j_hu on 5/7/17.
 */

public class UploadCommand extends Command implements Command.CommandHandler {

    private List<InOutRecord> notUploaded = new ArrayList<>();

    public UploadCommand() {
        super(Consts.COMMAND_REQ_INOUT_UPLOAD);
    }

    @Override
    protected String getType() {
        return "application/json";
    }

    @Override
    protected CommandHandler getHandler(String commandName) {
        CommandHandler handler = null;
        if (commandName != null) {
            handler = commandName.equals(Consts.COMMAND_RESP_INOUT_RECORD) ? this : null;
        }
        return handler;
    }

    @Override
    public boolean handle(InputStream input) {
        try {
            Gson gson = new Gson();
            InOutRecordRS rs = (InOutRecordRS)gson.fromJson(
                    getString(input),
                    new TypeToken<InOutRecordRS>(){}.getType());
            if (rs != null && rs.getList() != null && rs.getList().size() > 0) {
                List<Integer> successList = new ArrayList();
                for(InOutUploadResult result : rs.getList()) {
                    if (result.isSuccess()) {
                        successList.add(result.getId());
                    }
                }
                if (getDbHelper().setUploadSuccess(successList)) {
                    this.notify(NotifyMessage.info(
                            String.format(Consts.INFO_UPLOAD_RECORD_OK, rs.getUploadCount(), successList.size()), Consts.NOTIFY_DATA_SYNC, Consts.NOTIFY_DATA_SYC_VIEW_LIST));
                    this.notify(NotifyMessage.wrap(null, Consts.NOTIFY_CHECK_IN_OUT_LIST));
                } else {
                    this.notify(NotifyMessage.info(
                            String.format(Consts.INFO_UPLOAD_RECORD_FAIL, rs.getUploadCount(), rs.getUploadCount()), Consts.NOTIFY_DATA_SYNC, Consts.NOTIFY_DATA_SYC_VIEW_LIST));
                }
            }
        }catch (Exception e) {
            this.raiseError(e);
        }
        return true;
    }

    @Override
    protected OutputStream fill(OutputStream output) {
        DataOutputStream out = new DataOutputStream(output);
        try {
            if (notUploaded.size() > 0) {
                CommandRQ<ArrayList<InOutRecordRQ>> rq = new CommandRQ<>();
                ArrayList<InOutRecordRQ> list = new ArrayList<>(notUploaded.size());
                this.notify(NotifyMessage.info(
                        String.format("%s<%s>", Consts.INFO_UPLOAD_RECORD, notUploaded.size()), Consts.NOTIFY_DATA_SYNC, Consts.NOTIFY_DATA_SYC_VIEW_LIST));

                for(InOutRecord rec : notUploaded) {
                    list.add(new InOutRecordRQ(rec.getId(), rec.getMachine(), rec.getState(), rec.getNo(), rec.getSeq(), rec.isManual(), rec.getTimeStr()));
                }

                rq.setToken(getToken());
                rq.setData(list);

                byte[] p = new Gson().toJson(rq).getBytes();
                if (p != null && p.length > 0) {
                    out.write(p);
                }
            }
        } catch(IOException e) {
            raiseError(e);
        }
        return out;
    }

    @Override
    public boolean isAvailable() {
        boolean isAvail = false;
        if (Machine.getInstance().getType() == Machine.MachineType.Gate) {
            DBHelper helper = getDbHelper();
            if (helper != null) {
                notUploaded = getDbHelper().getNotUploadedRecords();
            }
            isAvail = notUploaded.size() > 0;
        }
        return isAvail;
    }

    private String getString(InputStream input) {
        String result = null;
        InputStreamReader in = null;
        try {
            String line;
            in = new InputStreamReader(input);
            StringBuffer sb = new StringBuffer();
            BufferedReader reader = new BufferedReader(in);
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            result = sb.toString();
        }catch(Exception e) {
            this.raiseError(e);
        } finally {
            if (in != null) {
                try {
                    in.close();
                }catch(IOException e) {
                    this.raiseError(e);
                }
            }
        }
        return result;
    }

    public class InOutRecordRQ {
        private int id;
        private int state;
        @SerializedName("holder_no")
        private int holderNo;
        @SerializedName("card_seq")
        private int seq;
        @SerializedName("is_manual")
        private boolean isManual;
        @SerializedName("create_time")
        private String createTime;
        private int machine;

        public InOutRecordRQ(int id, int machine, int state, int holderNo, int seq, boolean isManual, String createTime) {
            this.id = id;
            this.state = state;
            this.holderNo = holderNo;
            this.seq = seq;
            this.isManual = isManual;
            this.createTime = createTime;
            this.machine = machine;
        }
    }

    public class InOutRecordRS {
        @SerializedName("upload_count")
        private int uploadCount;
        private List<InOutUploadResult> list;

        public int getUploadCount() {
            return uploadCount;
        }

        public List<InOutUploadResult> getList() {
            return list;
        }
    }

    public class InOutUploadResult {
        private int id;
        private boolean success;

        public int getId() {
            return id;
        }

        public boolean isSuccess() {
            return success;
        }
    }
}
